<?php
/* {
Citru - www.citru.se
See license.txt for copyright and license information.
} */

session_start();
define ("_CITRU", TRUE);
$citruInstaller = new CitruInstaller();

class CitruInstaller {

	private $dbModule;

	function __construct() {
		mb_internal_encoding("utf-8");
		mb_http_output("utf-8");
		require_once('lib/FormBuilder.php');
	}
	

	public function getInstall($custom_stage=0, $error='') {
		$stage = (int)$_POST['stage'];
		if ((int)$custom_stage > 0) { 
			$stage = $custom_stage;
			$skip_checks = true;
		}
		$elements = array();
		
		switch($stage) {
			case 3:
				if(!$skip_checks) { $tableInstallErrors = self::installTables(); }
				if($tableInstallErrors != null) {
					return self::getInstall(2, '<p class="citru_error">'.$tableInstallErrors.'</p>');
				}
				
				if(!$skip_checks) { $newUserErrors = self::addNewUser(); }
				if($newUserErrors != null) {
					return self::getInstall(2, '<p class="citru_error">'.$newUserErrors.'</p>');
				}
				
				$elements[] = array('p', 'text'=> 'Your installation is complete!');
				$elements[] = array('p', 'text'=> 'Please remove install.php and inc/CitruInstaller.php before using citru.', 'after'=>'<br />');
				
				break;
			case 2:
				if(!$skip_checks) { $DbConnectionErrors = self::checkDbConnection(); }
				if($DbConnectionErrors != null) {
					return self::getInstall(1, '<p class="citru_error">'.$DbConnectionErrors.'</p>');
				}
				
				if(!$skip_checks) { $settingsFileErrors = self::createSettingsFile(); }
				if($settingsFileErrors != null) {
					return self::getInstall(1, '<p class="citru_error">'.$settingsFileErrors.'</p>');
				}
				
				$user = (string)$_POST['user'];
				$pass = (string)$_POST['pass'];
				
				$elements[] = array('form_start');
				$elements[] = array('hidden_custom', 'name'=>'stage', 'value'=>'3');
                
				$elements[] = array('fieldset_start', 'legend'=>'2. Create user');
				$elements[] = array('p', 'text'=>'Enter name and password for your administrator account.');
				
				$elements[] = array('label', 'for'=>'user', 'text'=>'Username:', 'after'=>'<br />');
				$elements[] = array('input_text', 'name'=>'user', 'value'=>$user, 'class'=>'citru_half_width', 'after'=>'<br />');
				$elements[] = array('label', 'for'=>'pass', 'text'=>'Password:', 'after'=>'<br />');
				$elements[] = array('input_password', 'name'=>'pass', 'value'=>$pass, 'class'=>'citru_half_width', 'after'=>'<br />');
				$elements[] = array('fieldset_end');
                
				$elements[] = array('footer_start');
				$elements[] = array('custom', 'data'=>$error);
				$elements[] = array('submit', 'value'=>'Next', 'icon'=>'next', 'order'=>'reversed');
				$elements[] = array('footer_end');
				$elements[] = array('form_end');
				break;
				
			default:
				$db_host = (string)$_POST['db_host'];
				if($db_host == '') { $db_host = 'localhost'; }
				$db_user = (string)$_POST['db_user'];
				$db_pass = (string)$_POST['db_pass'];
				$db_name = (string)$_POST['db_name'];
				
				$elements[] = array('p', 'text'=> 'Thank you for choosing Citru.');
				$elements[] = array('p', 'text'=> 'This guide will take you through the installation in four simple steps. Before you begin you\'ll need a mysql database and database user ready.');
				$elements[] = array('p', 'text'=> 'You might also want to read the <a href="doc/installing_citru.html">installation guide</a>.', 'after'=>'<br />');
				
				$elements[] = array('form_start');
				$elements[] = array('hidden_custom', 'name'=>'stage', 'value'=>'2');
				
				$elements[] = array('fieldset_start', 'legend'=>'1. Database settings');
				$elements[] = array('label', 'for'=>'db_type', 'text'=>'Database type:', 'after'=>'<br />');
				$elements[] = array('input_select', 'name'=>'db_type', 'options'=>array('MySql'), 'values'=>array('Mysql'), 'after'=>'<br />');
				$elements[] = array('label', 'for'=>'db_host', 'text'=>'Database host:', 'after'=>'<br />');
				$elements[] = array('input_text', 'name'=>'db_host', 'value'=>$db_host, 'class'=>'citru_half_width', 'after'=>'<br />');
				$elements[] = array('label', 'for'=>'db_user', 'text'=>'Database user:', 'after'=>'<br />');
				$elements[] = array('input_text', 'name'=>'db_user', 'value'=>$db_user, 'class'=>'citru_half_width', 'after'=>'<br />');
				$elements[] = array('label', 'for'=>'db_pass', 'text'=>'Database password:', 'after'=>'<br />');
				$elements[] = array('input_text', 'name'=>'db_pass', 'value'=>$db_pass, 'class'=>'citru_half_width', 'after'=>'<br />');
				$elements[] = array('label', 'for'=>'db_name', 'text'=>'Database name:', 'after'=>'<br />');
				$elements[] = array('input_text', 'name'=>'db_name', 'value'=>$db_name, 'class'=>'citru_half_width', 'after'=>'<br />');
				$elements[] = array('fieldset_end');
				
				$elements[] = array('footer_start');
				$elements[] = array('custom', 'data'=>$error);
				$elements[] = array('submit', 'value'=>'Next', 'icon'=>'next', 'order'=>'reversed');
				$elements[] = array('footer_end');
				$elements[] = array('form_end');
				break;
		}
		
		$form = FormBuilder::generateForm($elements,'Installer','');
		return $form;
	}

	private function checkDbConnection() {
		$db_type = (string)$_POST['db_type'];
		$db_host = (string)$_POST['db_host'];
		$db_user = (string)$_POST['db_user'];
		$db_pass = (string)$_POST['db_pass'];
		$db_name = (string)$_POST['db_name'];
		
		$dbModuleName = $db_type.'DbModule';
        
		try {
			require_once 'db/'.$dbModuleName.'.php';
			$this->dbModule = new $dbModuleName($db_host, $db_user, $db_pass, $db_name);
		} catch(Exception $e) {
			return $e->getMessage();
		}
		return null;
	}

	private function createSettingsFile() {
		$db_type = (string)$_POST['db_type'];
		$db_host = (string)$_POST['db_host'];
		$db_user = (string)$_POST['db_user'];
		$db_pass = (string)$_POST['db_pass'];
		$db_name = (string)$_POST['db_name'];
		
		$data = <<<EOT
<?php 
/* {
Citru - www.citru.se
See license.txt for copyright and license information.
} */

if(!defined("_CITRU")) {die('This file should not be accessed directly.');}

// Database settings
\$db_type = '$db_type';
\$db_host = '$db_host';
\$db_user = '$db_user';
\$db_pass = '$db_pass';
\$db_name = '$db_name';

\$this->loadDatabase(\$db_type, \$db_host, \$db_user, \$db_pass, \$db_name);

// Load modules
\$this->loadModule('TextModule');
\$this->loadModule('NavigationModule');
\$this->loadModule('PictureModule');
\$this->loadModule('UserModule');
\$this->loadModule('PageModule');
\$this->loadModule('AdminNavModule');

// Load Internal libraries
\$this->loadLib('FormBuilder');	// Used by most modules

// Load External libraries
\$this->loadLib('Textile');		// Used in TextModule
?>
EOT;
		
		if(file_exists(getcwd().'/config/settings.php')) {
			return 'You already have a settings file: config/settings.php';
		}
		
		if(!is_writable(getcwd().'/config')) {
			return 'Could not write to config directory. Set permissions to 777.';
		}
		
		$handle = fopen(getcwd().'/config/settings.php', "w");
		
		if (fwrite($handle, $data) === FALSE) {
			return 'Could not create settings file.';
		}

		fclose($handle);
		
		return null;
	}
	
	private function installTables() {
		// Init. database module and libs.
		require_once(getcwd().'/config/settings.php');
		
		// Install Permissions table
		$params_perm = array(	"table" => 'Permissions',
								"columns" => array( array('id', 'int(11) NOT NULL auto_increment'),
													array('module', 'text NOT NULL'),
								 					array('instance', 'text NOT NULL'),
													array('permissions', 'text NOT NULL')),
								"primary_key" => 'id');
		if(!$this->dbModule->addTable($params_perm)) {
			return 'Could not create permissions table.';
		}

		// Install Users table
		$params_users = array(	"table" => 'Users',
								"columns" => array( array('id', 'int(11) NOT NULL auto_increment'),
													array('name', 'tinytext NOT NULL'),
								 					array('hash', 'text NOT NULL'),
													array('salt', 'text NOT NULL'),
													array('groups', 'text NOT NULL')),
								"primary_key" => 'id');
		if(!$this->dbModule->addTable($params_users)) {
			return 'Could not create users table.';
		}
		
		// Install Pages table
		// id, template, page, title
		$params_users = array(	"table" => 'Pages',
								"columns" => array( array('id', 'int(11) NOT NULL auto_increment'),
													array('template', 'text NOT NULL'),
								 					array('page', 'text NOT NULL'),
													array('title', 'text NOT NULL')),
								"primary_key" => 'id');
		if(!$this->dbModule->addTable($params_users)) {
			return 'Could not create pages table.';
		}
		
		$all_permissions = '1111';
		
		try {
			// Install TextModule
			$textModule = new TextModule('', $this->dbModule, $all_permissions);
			$textModule->installTables();

			// Install PictureModule
			$pictureModule = new PictureModule('', $this->dbModule, $all_permissions);
			$pictureModule->installTables();

			// Install NavigationModule
			$navigationModule = new NavigationModule('', $this->dbModule, $all_permissions);
			$navigationModule->installTables();
			
			// Install UserModule
			$userModule = new UserModule('', $this->dbModule, $all_permissions);
			$userModule->installTables();

			// Install AdminNavModule
			$admModule = new AdminNavModule('', $this->dbModule, $all_permissions);
			$admModule->installTables();
			
			// Install PageModule
			$pageModule = new PageModule('', $this->dbModule, $all_permissions);
			$pageModule->installTables();

		} catch(Exception $e) {
			return $e->getMessage();
		}
		
		
		return null;
	}
	
	private function addNewUser() {
		$user = (string)$_POST['user'];
		$pass = (string)$_POST['pass'];
		
		if(strlen($user) < 4 || strlen($user) > 20) {
			return 'Username must be between 4 and 20 characters.';
		}
		
		if(preg_match('/[^A-Za-z0-9-_]+/', $user)) {
			return 'Username can only contain letters, numbers and dashes.';
		}
		
		if(strlen($pass) < 8 || strlen($pass) > 80) {
			return 'Password must be between 8 and 80 characters.';
		}
		
		$pass_small = (int)(boolean)preg_match('/[a-z]/', $pass);
		$pass_big = (int)(boolean)preg_match('/[A-Z]/', $pass);
		$pass_nums = (int)(boolean)preg_match('/[0-9]/', $pass);
		$pass_other = (int)(boolean)preg_match('/[^A-Za-z0-9]/', $pass);
		
		if(($pass_small + $pass_big + $pass_nums + $pass_other) < 2) {
			return 'Password must contain at least one character from two of the following groups: lowercase, uppercase, numbers and special characters.';
		}
		
		// Generate salt and hash
		$salt = sha1(uniqid(rand(), true));
		$pass_hash = sha1($pass);
		$hash = sha1($pass_hash.$salt);
		
		$params = array(	'table' => 'Users',
        					'set' => array(	'name'=>$user,
											'hash'=>$hash,
											'salt'=>$salt,
											'groups'=>'@admin'));
        if(!$this->dbModule->insertRow($params)) {
			return 'Could not add user to Users table.';
		}
		return null;
	}
	
	// ### ___ Load functions _________________
	// ### Methods that loads php files
	
	// Load database: Used (from settings.php) to load a database module
	private function loadDatabase($db_type='', $db_host='localhost', $db_user='', $db_pass='', $db_name='') {
		$dbModuleName = $db_type.'DbModule';
		
		require_once 'db/'.$dbModuleName.'.php';
		$this->dbModule = new $dbModuleName($db_host, $db_user, $db_pass, $db_name);
	}
	
	// Load module: Installs a module
	private function loadModule($name) {
		require_once 'modules/'.$name.'.php';
	}

	// Load lib: Used (from settings.php) to load an external library
	private function loadLib($name) {
		require_once 'lib/'.$name.'.php';
	}
}
?>
